﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;

namespace SaiLing
{
    /// <summary>
    /// 
    /// </summary>
    public static class StringExtensions
    {
        #region Methods
        /// <summary>
        /// Ellipsises the specified STR.
        /// </summary>
        /// <param name="str">The STR.</param>
        /// <param name="length">The length.</param>
        /// <param name="ellipsisLength">Length of the ellipsis.</param>
        /// <returns></returns>
        public static string Ellipsis(this string str, int length, int ellipsisLength = 3)
        {
            if (str == null)
                return null;
            return str.Length <= length ? str : str.Substring(0, length - ellipsisLength).PadRight(length, '.');
        }

        /// <summary>
        /// Equalses the or null empty.
        /// </summary>
        /// <param name="str1">The STR1.</param>
        /// <param name="str2">The STR2.</param>
        /// <param name="comparisonType">Type of the comparison.</param>
        /// <returns></returns>
        public static bool EqualsOrNullEmpty(this string str1, string str2, StringComparison comparisonType)
        {
            return String.Compare(str1 ?? "", str2 ?? "", comparisonType) == 0;
        }

        /// <summary>
        /// Trims the or null.
        /// </summary>
        /// <param name="str">The STR.</param>
        /// <returns></returns>
        public static string TrimOrNull(this string str)
        {
            if (str == null)
                return null;

            return str.Trim();
        }

        /// <summary>
        /// Merges the name.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        public static string MergeName(this string name)
        {
            return name.Replace(" ", String.Empty);
        }

        /// <summary>
        /// Splits the name.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="toLower">if set to <c>true</c> [to lower].</param>
        /// <returns></returns>
        public static string SplitName(this string name, bool toLower = true)
        {
            var builder = new StringBuilder();
            for (int i = 0; i < name.Length; i++)
            {
                var ch = name[i];
                if (ch >= 'A' && ch <= 'Z' && i > 0)
                {
                    var prev = name[i - 1];
                    if (prev != ' ')
                    {
                        if (prev >= 'A' && prev <= 'Z')
                        {
                            if (i < name.Length - 1)
                            {
                                var next = name[i + 1];
                                if (next >= 'a' && next <= 'z')
                                {
                                    builder.Append(' ');
                                }
                            }
                        }
                        else
                        {
                            builder.Append(' ');
                        }
                    }
                    builder.Append(toLower ? ch.ToString(CultureInfo.InvariantCulture).ToLower() : ch.ToString(CultureInfo.InvariantCulture));
                }
                else
                {
                    builder.Append(ch);
                }
            }
            return builder.ToString();
        }
        /// <summary>
        /// Ases the specified source.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source">The source.</param>
        /// <returns></returns>
        public static T As<T>(this string source)
        {
            if (source == null)
            {
                return default(T);
            }

            try
            {
                return (T)Convert.ChangeType(source, typeof(T));
            }
            catch
            {
                return default(T);
            }
        }

        public static T As<T>(this string source, T defaultValue)
        {
            if (source == null)
            {
                return defaultValue;
            }

            try
            {
                return (T)Convert.ChangeType(source, typeof(T));
            }
            catch
            {
                return defaultValue;
            }
        }

        /// <summary>
        /// Ases the specified source.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="type">The type.</param>
        /// <returns></returns>
        public static object As(this string source, Type type)
        {
            if (source == null)
            {
                return null;
            }

            try
            {
                return Convert.ChangeType(source, type);
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Itemses the specified source.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="itemIndex">Index of the item.</param>
        /// <param name="separator">The separator.</param>
        /// <returns></returns>
        public static string Items(this string source, int itemIndex, string separator = ",")
        {

            if (source == null)
            {
                return string.Empty;
            }
            var items = source.Split(new[] { separator }, StringSplitOptions.RemoveEmptyEntries);
            return items.Length > itemIndex ? items[itemIndex] : string.Empty;
        }

        /// <summary>
        /// Determines whether [contains] [the specified original].
        /// </summary>
        /// <param name="original">The original.</param>
        /// <param name="value">The value.</param>
        /// <param name="comparisionType">Type of the comparision.</param>
        /// <returns>
        ///   <c>true</c> if [contains] [the specified original]; otherwise, <c>false</c>.
        /// </returns>
        public static bool Contains(this string original, string value, StringComparison comparisionType)
        {
            return original.IndexOf(value, comparisionType) >= 0;
        }

        /// <summary>
        /// Strips the HTML XML tags.
        /// </summary>
        /// <param name="content">The content.</param>
        /// <returns></returns>
        public static string StripHtmlXmlTags(this string content)
        {
            if (string.IsNullOrEmpty(content))
            {
                return content;
            }
            return Regex.Replace(content, "<[^>]+>?", "", RegexOptions.IgnoreCase | RegexOptions.Compiled);
        }

        /// <summary>
        /// Strips all tags.
        /// </summary>
        /// <param name="stringToStrip">The string to strip.</param>
        /// <returns></returns>
        public static string StripAllTags(this string stringToStrip)
        {
            if (string.IsNullOrEmpty(stringToStrip))
            {
                return stringToStrip;
            }
            stringToStrip = StripHtmlXmlTags(stringToStrip);
            // paring using RegEx
            //
            stringToStrip = Regex.Replace(stringToStrip, "</p(?:\\s*)>(?:\\s*)<p(?:\\s*)>", "\n\n", RegexOptions.IgnoreCase | RegexOptions.Compiled);
            stringToStrip = Regex.Replace(stringToStrip, "<br(?:\\s*)/>", "\n", RegexOptions.IgnoreCase | RegexOptions.Compiled);
            stringToStrip = Regex.Replace(stringToStrip, "\"", "''", RegexOptions.IgnoreCase | RegexOptions.Compiled);


            return stringToStrip;
        }

        public static string UrlEncode(this string source)
        {
            return HttpUtility.UrlEncode(source);
        }

        public static string UrlDecode(this string source)
        {
            return HttpUtility.UrlDecode(source);
        }

        public static string ToUnicodeJs(this string source)
        {
            var outStr = "";
            if (!string.IsNullOrEmpty(source))
            {
                for (var i = 0; i < source.Length; i++)
                {
                    if (Regex.IsMatch(source[i].ToString(CultureInfo.InvariantCulture), @"[\u4e00-\u9fa5]")) { outStr += @"\u" + ((int)source[i]).ToString("x"); }
                    else { outStr += source[i]; }
                }
            }
            return outStr;
        }

        public static string GenerateSalt()
        {
            var data = new byte[16];
            new RNGCryptoServiceProvider().GetBytes(data);
            return Convert.ToBase64String(data);
        }

        public static string EncryptPassword(this string pass, string salt)
        {

            byte[] bytes = Encoding.Unicode.GetBytes(pass);
            //byte[] src = Convert.FromBase64String(salt);
            //byte[] inArray = null;
            //KeyedHashAlgorithm algorithm = HashAlgorithm .Create();
            //algorithm.Key = new byte[64]; //compatible with mono
            //if (algorithm.Key.Length == src.Length)
            //{
            //    algorithm.Key = src;
            //}
            //else if (algorithm.Key.Length < src.Length)
            //{
            //    byte[] dst = new byte[algorithm.Key.Length];
            //    Buffer.BlockCopy(src, 0, dst, 0, dst.Length);
            //    algorithm.Key = dst;
            //}
            //else
            //{
            //    int num2;
            //    byte[] buffer5 = new byte[algorithm.Key.Length];
            //    for (int i = 0; i < buffer5.Length; i += num2)
            //    {
            //        num2 = Math.Min(src.Length, buffer5.Length - i);
            //        Buffer.BlockCopy(src, 0, buffer5, i, num2);
            //    }
            //    algorithm.Key = buffer5;
            //}
            //inArray = algorithm.ComputeHash(bytes);
            HMACSHA256 hashAlgorithm = new HMACSHA256(bytes);

            var inArray=hashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(salt));
            return Convert.ToBase64String(inArray);
        }

        public static string Md5(this string source)
        {
            var hash = MD5.Create().ComputeHash(Encoding.ASCII.GetBytes(source));
            var stringBuilder = new StringBuilder();
            foreach (var t in hash)
                stringBuilder.Append(t.ToString("X2"));
            return stringBuilder.ToString();
        }

        public static string Md5(this string source, Encoding encoding)
        {
            var hash = MD5.Create().ComputeHash(encoding.GetBytes(source));
            var stringBuilder = new StringBuilder();
            foreach (var t in hash)
                stringBuilder.Append(t.ToString("X2"));
            return stringBuilder.ToString();
        }

        public static string Sha1(this string source, Encoding encoding)
        {
            var buffer = encoding.GetBytes(source);
            var data = SHA1.Create().ComputeHash(buffer);
            var sb = new StringBuilder();
            foreach (var t in data)
            {
                sb.Append(t.ToString("X2"));
            }
            return sb.ToString().ToLower();
        }

        public static string GenerateRandomDigitCode(int length)
        {
            Random random = new Random();
            string str = string.Empty;
            for (int index = 0; index < length; ++index)
                str += random.Next(10).ToString();
            return str;
        }

        #endregion

        //public static string ToGeometryString(this string source, DbGeometryType type)
        //{
        //    var result = string.Empty;
        //    if (string.IsNullOrWhiteSpace(source))
        //        return result;
        //    var points = source.Split(',');
        //    var values = new List<string>();
        //    for (var i = 0; i < points.Length; i += 2)
        //    {
        //        values.Add($"{points[i]} {points[i + 1]}");
        //    }
        //    switch (type)
        //    {
        //        case DbGeometryType.Point:
        //            result = $"POINT({string.Join(",", values)})";
        //            break;
        //        case DbGeometryType.Line:
        //            result = $"LINESTRING({string.Join(",", values)})";
        //            break;
        //        case DbGeometryType.Polygon:
        //            if (values.Any() && values.First() != values.Last())
        //            {
        //                values.Add(values.First());
        //            }
        //            result = $"POLYGON(({string.Join(",", values)}))";
        //            break;
        //    }
        //    return result;
        //}

        public static string ContinuityCode(this string source, int length)
        {
            var start = 0;
            if (!string.IsNullOrWhiteSpace(source))
                start = Convert.ToInt32(source);
            start += 1;
            var code = start.ToString();
            if (code.Length < length)
                code = code.PadLeft(length, '0');
            return code;
        }

        public static Stream ToStream(this string source)
        {
            if (string.IsNullOrWhiteSpace(source))
                return null;
            var bytes = Encoding.Default.GetBytes(source);
            return new MemoryStream(bytes);
        }
    }
}
